/*
 * DragAndDropListener.java
 *
 * Created on Sobota, 2007, oktober 27, 20:58
 *
 */

package gui.editor;

import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

/**
 * trieda sluzi na zachytavanie eventov mysky - stlacanie tlacidiel mysky a pohyb mysky
 * @author baracuda
 */
public class DragAndDropListener implements MouseListener, MouseMotionListener{
    /**
     * last_x x-ova suradnica poslednej pozicie buttonu na paneli
     */
    private int last_x = 0;//posledna  X pozicia komponenety
    
    /**
     * last_y y-ova suradnica poslednej pozicie buttonu na paneli
     */    
    private int last_y = 0;//posledna Y  pozicia komponenety
    
    /**
     * first_x x-ova suradnica buttonu, ked sa na nom prvy krat vyskytol event mousePresed <br />
     * pouziva sa ako pozicia, od ktorej sa prepocitava nova pozicia (podla pohybu mysky)
     */
    private int first_x = 0;
    
   /**
     * first_y y-ova suradnica buttonu, ked sa na nom prvy krat vyskytol event mousePresed <br />
     * pouziva sa ako pozicia, od ktorej sa prepocitava nova pozicia (podla pohybu mysky)
     */
    private int first_y = 0;
    
    /**
     * panel referencia a skutocny panel, je tu len kvoli tomu, aby sa rychlejsie dalo pristupovat na panel, na ktorom sa vyskytuju nody, linky a eventy
     */
    private EditorPanel panel;
    /**
     * canCreateNewLinkMenuItem item menu, ktory sluzi na vytvorenie novej linky, je tu preto, aby sa lahsie nastavovalo jeho enabled stav
     */
    
    private PolozkaMenuButton canCreateNewLinkMenuItem;
    
    /**
     * canBeDragged boolean, ktory urcuje, ci je mozne buttonom, na ktorom bol vyvolany event mousePressed dragovat alebo nie
     * (pri vytvarani novej linky sa na button klikne, ale nemalo by byt mozne s tymto buttonom hybat)
     */
    private boolean canBeDragged = false;
    
    /**
     * vytvori instanciu DragAndDropListener a priradi panel do privatnej premennej - pre dalsi lahsi pristup
     * @param panel panel, na ktorom su zaznamenavane eventy
     */
    
    public DragAndDropListener(JPanel panel) {
        this.panel = (EditorPanel) panel;
    }
    
    /**
     * metoda nerobi nic
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mouseClicked(MouseEvent mouseEvent) {
    }
    
    /**
     * vyvola sa pri stlaceni mysky nad buttonom, rozlisuju sa tieto akcie: <br />
     * <ol>
     *  <li><b>sltaceny lavy button</b> 
     *    <ul> 
     *      <li></li> ak vytvaram novu linku, pokusi sa vytvorit linku medzi selectnutym buttonom a buttonom, na ktory sa prave kliklo
     *      <li></li> ak nevytvaram novu linku, nastavi <i>canBeDragged</i> na true a zapamata si suradnice
     *    </ul>
     *  </li>
     *  <li><b>stlaceny pravy button</b>
     *      vyskoci popup menu nad buttonom, zistuje sa, ci je mozne vytvorit konekciu nad danym buttonom a ak nie, vysedivie moznost vytvorenia novej linky
     *  </li>
     * </ol>
     * <br />
     * ak sa kliklo na iny button, aky bol predtym selectnuty a nevytvara sa nova linka, selectnuty button sa odselectne a button, na ktory sa prave kliklo
     * sa oznaci za selecnuty
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mousePressed(MouseEvent mouseEvent) {
        boolean potvrditSelectnutie = true; //aby ked sa vytvori nova linka sa nepreplo na novozvoleny button
        
        if(mouseEvent.getButton() == mouseEvent.BUTTON1) { //zmackol lavy button
            if(panel.isVytvaramNovuLinku()) { //nasiel som par, snazim sa vytvorit linku medzi nimi
                //pokus o vytvorenie novej linky medzi selecnutym buttonom a novym
                try {
                    panel.pridajLinku(panel.getSelectnutyNodeButton(), ((NodeButton) mouseEvent.getSource()));
                } catch (Exception e) {
                    panel.fireExceptionCaught(e);
                }
                //vizualizacia
                potvrditSelectnutie = false; //aby ked sa vytvori nova linka sa nepreplo na novozvoleny button
                mouseExited(mouseEvent); //najprv sa vyvola event, aby sa zase zmazalo vyznacenie gombika
                panel.setVytvaramNovuLinku(false); //uz som prestal vytvarat novu linku
            } else { //nevytvaram novu linku - mozem dragovat
                canBeDragged = true;
                last_x  = ((Component)mouseEvent.getSource()).getX() - mouseEvent.getX();
                last_y  = ((Component)mouseEvent.getSource()).getY()-  mouseEvent.getY();
                first_x = mouseEvent.getX(); 
                first_y = mouseEvent.getY();
            }
        } else if (mouseEvent.getButton() == mouseEvent.BUTTON3) {
            if (((NodeButton)mouseEvent.getSource()).getPopup() == null) { //vytvorenie popup menu - staci vytvarat RAZ
                PolozkaMenuButton menuItem;
                
                ((NodeButton)mouseEvent.getSource()).setPopup(new JPopupMenu());
                
                canCreateNewLinkMenuItem = new PolozkaMenuButton("Create connection", 0, ((NodeButton)mouseEvent.getSource()));
                canCreateNewLinkMenuItem.addActionListener(new MenuItemActionListener(panel));
                ((NodeButton)mouseEvent.getSource()).getPopup().add(canCreateNewLinkMenuItem);
                
                menuItem = new PolozkaMenuButton("Delete", 1, ((NodeButton)mouseEvent.getSource()));
                menuItem.addActionListener(new MenuItemActionListener(panel));
                ((NodeButton)mouseEvent.getSource()).getPopup().add(menuItem);
                
                //zaregistrovanie menu pre button
                ((NodeButton)mouseEvent.getSource()).setComponentPopupMenu(((NodeButton)mouseEvent.getSource()).getPopup());
            }
            
            if(((NodeButton)mouseEvent.getSource()).getNode().canCreateNewLink() == null) {
                canCreateNewLinkMenuItem.setEnabled(true);
            } else {
                canCreateNewLinkMenuItem.setEnabled(false);
            }
        }
        
        if(potvrditSelectnutie) { //ak urcite mozem prekliknut na novy button           
            if(panel.getSelectnutyNodeButton() == null) {
                panel.setSelectnutyNodeButton((NodeButton)mouseEvent.getSource()); //nastavenie aktivneho buttonu
                panel.getSelectnutyNodeButton().select();
            } else {
                if( !((NodeButton)mouseEvent.getSource()).equals(panel.getSelectnutyNodeButton()) ) {
                    panel.getSelectnutyNodeButton().unselect();
                    panel.setSelectnutyNodeButton((NodeButton)mouseEvent.getSource()); //prestavenie aktivneho buttonu
                    ((NodeButton)mouseEvent.getSource()).select();
                }
            }
        }
    }
    
    /**
     * v pripade, ze sa pustilo lave tlacidlo mysi, nastavi sa premenna <i>canBeDragged</i> na <i>false</i>
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mouseReleased(MouseEvent mouseEvent) {
        if(mouseEvent.getButton() == mouseEvent.BUTTON1) {
            canBeDragged = false;
        }
    }
    
    /**
     * vyvola sa, ak mys vstupila nad button. 
     * V pripade, ze sa vytvara nova linka a zaroven mys nie je nad prave selectnutym buttonom "vysvieti sa" tento button zmenou ikony
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mouseEntered(MouseEvent mouseEvent) {
        if(panel.isVytvaramNovuLinku() && (((NodeButton)mouseEvent.getSource()) != panel.getSelectnutyNodeButton())) { //iba ak vytvaram novu linku a nie som na prave selectnutom buttone
            ((NodeButton)mouseEvent.getSource()).select();
        }
        
    }
    
    /**
     * vyvola sa, ak mys opustila button.
     * V pripade, ze sa vytvara nova linka a zaroven mys nebola nad prave selectnuym buttonom "odsvieti sa" tento button zmenou ikony
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mouseExited(MouseEvent mouseEvent) {
        if(panel.isVytvaramNovuLinku() && (((NodeButton)mouseEvent.getSource()) != panel.getSelectnutyNodeButton())) {
            ((NodeButton)mouseEvent.getSource()).unselect();
        }
    }
    
    /**
     * ak je premenna <i>canBeDragged</i> nastavena na true, vyrataju sa nove suradnice, kam sa ma button presunut. Button sa moze pohybovat iba na ploche
     * panelu, preto je zakazane hybat buttonom miomo panelu. Na konci prebehne prekreslenie panelu.
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mouseDragged(MouseEvent mouseEvent) {
        if(canBeDragged) {
            last_x = ((Component)mouseEvent.getSource()).getX()-first_x; //vypocita sa nova suradnica buttonu, pomocou zmeny pozicie mysky a povodnej pozicie buttonu
            last_y = ((Component)mouseEvent.getSource()).getY()-first_y;
            if((last_x + mouseEvent.getX() + (EditorPanel.radius)*2 < panel.max_width) && (last_y + mouseEvent.getY() + (EditorPanel.radius)*2 < panel.max_height) && (last_y + mouseEvent.getY() >0) && (last_x + mouseEvent.getX() > 0)) { //podmienka, aby sa nemohlo vyskreslovat mimo plochu
                ((Component)mouseEvent.getSource()).setLocation(last_x + mouseEvent.getX(), last_y+  mouseEvent.getY()); //prekreslenie buttonu na inu pozicu
                ((NodeButton)mouseEvent.getSource()).getNode().setCenterXY(((Component)mouseEvent.getSource()).getX()+(EditorPanel.radius),((Component)mouseEvent.getSource()).getY()+(EditorPanel.radius)); //nastavi sa pozicia stredu buttonu do jeho nodeu
            }
        }
        panel.updatePanel();
    }
    
    /**
     * metoda sa nepouziva
     * @param mouseEvent event, ktory sa vyvolal
     */
    public void mouseMoved(MouseEvent mouseEvent) {
    }
    
    /**
     * metoda vracia panel, na ktorom je tento listener zaregistrovany
     * @return panel, na ktorom sa vykresluje
     */
    public JPanel getPanel() {
        return panel;
    }
    
    /**
     * nastavi lokalnu premennu panel na Editor panel
     * @param panel EditorPanel, na ktory sa vykresluje a tento listener je na neho zaregistrovany
     */
    public void setPanel(JPanel panel) {
        this.panel = (EditorPanel) panel;
    }
}